package com.hcw.bankdemob.service.impl;

import com.hcw.bankdemob.mapper.AccountMapper;
import com.hcw.bankdemob.mapper.TradeLogMapper;
import com.hcw.bankdemob.model.Account;
import com.hcw.bankdemob.model.TradeLog;
import com.hcw.bankdemob.service.DBbAccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;

/**
 * @ClassName DBbAccountService
 * @description: B库的服务
 * @author: hcw
 * @time: 2020/8/29 16:50
 */
@Slf4j
@Service
public class DBbAccountServiceImpl implements DBbAccountService {
    @Resource
    AccountMapper accountMapper;
    @Resource
    TradeLogMapper tradeLogMapper;

    /**
     * @param toName
     * @param money
     * @return 为了方便 另外A和B服务返回参数较简单，没有设计成包含错误码和具体错误类型的对象
     * @description: 收款方的服务
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,timeout = 10, rollbackFor = Exception.class,isolation= Isolation.READ_COMMITTED)
    public boolean toAccoutTransfer(String fromName,String toName, BigDecimal money,int aId) {
        try {
            log.info("在数据库里面记录一下这条数据DATA_B1,方便对账做凭证和逆向冲红查重,交易号唯一");
            TradeLog tradeLog = TradeLog.builder().aId(aId).dateTime(new Date()).fromName(fromName).money(money).status(0).toName(toName).build();
            tradeLogMapper.insertTradeLog(tradeLog);
            //收款方B款账户
            Account b = accountMapper.getAccountByNameLock(toName);
            if (b == null || b.getStatus() == 0) {
                //更新数据库转账交易记录DATA_B1，更新为失败，
                log.info(toName + "账户异常");
                //如果是同步调用就需要回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return false;
            }
            //给B账户加钱
            BigDecimal currentMoneyB = b.getCurrentMoney();
            currentMoneyB = currentMoneyB.add(money);
            int statusB = accountMapper.updateAccountByAccountName(toName, currentMoneyB);
            if (statusB == 0) {
                //更新数据库转账交易记录DATA_B1，更新为失败，记录失败原因
                log.info("收款账户账户更新异常，请检查sql日志");
                tradeLogMapper.updateTradeLogById(tradeLog.getId(), 2, "收款账户账户更新异常");
                //如果是同步调用就需要回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return false;
            }
            //异常测试
//            int[] arr=  new int[1];
//            arr[2] =3;
            //更新数据库转账交易记录DATA_B1，更新为成功，逆向订单查询以防重复。
            tradeLogMapper.updateTradeLogById(tradeLog.getId(), 1, "");
            log.info(fromName+"转款给"+toName+",并且"+toName+"数据库更新成功");
            return true;
        } catch (Exception e) {
            /**这里涉及两个逻辑
             * 1.AB同步调用，B这里直接返回 false; 让A 判断false后直接回滚。
             * 2.AB异步调用，B这里就需要调用A预留给B的逆向回滚接口。
             */
            //此处为AB同步调用
            //更新数据库转账交易记录DATA_B1，更新为失败，记录失败原因
            log.info("系统错误：", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return false;

            //2.AB异步调用
            //如果整个过程出现异常那么就调用转款方的逆向接口，然后这个事务也回滚
//            try {
//               boolean result=  accountServiceImpl.noFromAccoutTransfer(fromName, toName, money);
//            } catch (Exception e1) {
//                /**
//                 * 1.如果调用逆向接口出现超时或者网络等问题，还是回滚次事务，不给b账户加钱。
//                 * 2.记录一下这次调用失败操作，最好是存在数据库里面，然后定时处理或者手动处理一下。
//                 * 这里的失败涉及两个问题 1.转款方A服务是执行成功了，但是传输过程出问题了。这个需要调用逆向回滚A数据
//                 *                    2.转款发的接口根本没有执行，不需要调用逆向回滚。
//                 *
//                 *处理逻辑:  在转款方AaccountServiceImpl.noFromAccoutTransfer做一个处理的记录。 然后这需要查询一次，再处理，防止重复。
//                 * 1、如果是已经处理过了就什么都不用，直接抛出异常回滚。
//                 *
//                 * 2、如果A那边没有处理过，那么这边可以再试一次，accountServiceImpl.noFromAccoutTransfer(fromName, toName, money);
//                 *  如果还是不行 就记录下来手动操作。
//                 * //因为没有写日志到数据库 我这就只处理第1个逻辑。
//                 */
//
//                log.info(fromName+",调用转款逆向接口accountServiceImpl.noFromAccoutTransfer出错");
//                throw new ServiceException(e1.getMessage(), ExceptionEnum.SERVER_From_RPC_ERROR);
//            }
//
//            throw new ServiceException(e.getMessage(), ExceptionEnum.SERVER_TRANT_ERROR,e);
        }
    }

    /**
     * @param toName
     * @description: 收款方的逆向服务（收款成功后 ，转款发服务失败了 回滚收款方数据）
     * 主要用于异步调用和网络等异常情况
     * @return: 为了方便 另外A和B服务返回参数较简单，没有设计成包含错误码和具体错误类型的对象
     * @author: hcw
     * @time: 2020/8/29 16:35
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,timeout = 5, rollbackFor = Exception.class)
    public boolean noToAccoutTransfer(String fromName,String toName, BigDecimal money,int id) {
        /**
         * 执行逻辑
         * 1.首先要查询转账记录DATA_B1,防止重复
         * 2.验证通过后，再更新数据库
         */
        return false;
    }
}